Imports Microsoft.Xna.Framework
Imports Microsoft.Xna.Framework.Graphics
Imports System.Collections.Generic
Imports Microsoft.Xna.Framework.Audio
Imports Microsoft.Xna.Framework.Media

''' <summary>
''' Controls playback of an Animation
''' </summary>
Public Structure AnimationSequence
	Private firstTime As Boolean
	Public sequence As Sequence
	Public lsequence As List(Of Sequence)
	Private TotalElapsed As Single
	'private float time;


	Public Shared frameRate As Single = 0.09F


	''' <summary>
	''' 
	''' </summary>
	Public ReadOnly Property IsStoppable() As Boolean
		Get
			If sequence IsNot Nothing Then
				Return Frames(m_frameIndex).stoppable
			End If
			Return True
		End Get
	End Property
	'set { stoppable = value; }

	Public ReadOnly Property Frames() As List(Of Frame)
		Get
			Return sequence.frames
		End Get
	End Property

	''' <summary>
	''' Gets the index of the current frame in the animation.
	''' </summary>
	Public Property FrameIndex() As Integer
		Get

			Return m_frameIndex
		End Get
		Set
			m_frameIndex = value
		End Set
	End Property
	Private m_frameIndex As Integer




	''' <summary>
	''' Gets a texture origin at the bottom center of each frame.
	''' </summary>
	Public ReadOnly Property Origin() As Vector2
		Get
			If sequence.frames(m_frameIndex).texture Is Nothing Then
				Return Vector2.Zero
			End If
			Return New Vector2(sequence.frames(m_frameIndex).texture.Width / 2F, 0)
		End Get
	End Property


	''' <summary>
	''' Gets the index of the current frame in the animation.
	''' i count ONLY sprite command
	''' </summary>
	Public Function FrameSpriteCount() As Integer
		Dim frameSprite As Integer = 0
		For Each f As Frame In Me.Frames
			If f.type = Enumeration.TypeFrame.SPRITE Then
				frameSprite += 1
			End If
		Next
		Return frameSprite
	End Function

	''' <summary>
	''' Begins or continues playback of an animation.
	''' </summary>
	Public Sub PlayAnimation(lsequence As List(Of Sequence), stateElement As StateElement)
		Dim statePlayerElement As StatePlayerElement
		Dim stateTileElement As StateTileElement
		Dim stateName As String = String.Empty
		If GetType(StatePlayerElement) Is stateElement.[GetType]() Then
			statePlayerElement = DirectCast(stateElement, StatePlayerElement)
			stateName = statePlayerElement.state.ToString()
		ElseIf GetType(StateTileElement) Is stateElement.[GetType]() Then
			stateTileElement = DirectCast(stateElement, StateTileElement)
			stateName = stateTileElement.state.ToString()
		Else
			stateName = stateElement.state.ToString()
		End If


		' Start the new animation.
		If stateElement.Priority = Enumeration.PriorityState.Normal And Me.IsStoppable = False Then
			Return
		End If

		'Check if the animation is already playing
		If sequence IsNot Nothing AndAlso sequence.name = stateName Then
			Return
		End If



		Me.lsequence = lsequence
		'Search in the sequence the right type
		Dim result As Sequence = lsequence.Find(Function(s As Sequence) s.name = stateName.ToUpper())

		If result Is Nothing Then
			'will be an error 
			Return
		End If

		'cloning for avoid reverse pemanently...
		sequence = result.DeepClone()

		If stateElement.Stoppable IsNot Nothing Then
			For Each f As Frame In sequence.frames
				f.stoppable = CBool(stateElement.Stoppable)
			Next
		End If

		'For increase offset depend of the state previus; for example when running and fall the x offset will be increase.
		If stateElement.OffSet <> Vector2.Zero Then
			For Each f As Frame In sequence.frames
				f.xOffSet = f.xOffSet + CInt(Math.Truncate(stateElement.OffSet.X))
				f.yOffSet = f.yOffSet + CInt(Math.Truncate(stateElement.OffSet.Y))
			Next
		End If

		'Check if reverse movement and reverse order and sign x,y
		If stateElement.Reverse = Enumeration.SequenceReverse.Reverse Then
			Dim newListFrame As New List(Of Frame)()
			Dim newListCommand As New List(Of Frame)()

			For Each f As Frame In sequence.frames
				If f.type = Enumeration.TypeFrame.COMMAND Then
					newListCommand.Add(f)
				Else
					f.xOffSet = -1 * f.xOffSet
					f.yOffSet = -1 * f.yOffSet
					newListFrame.Add(f)
				End If
			Next
			newListFrame.Reverse()
			'add command
			For Each f As Frame In newListCommand
				newListFrame.Add(f)
			Next

			sequence.frames = newListFrame
		End If


		If stateElement.Reverse = Enumeration.SequenceReverse.FixFrame Then
			Me.m_frameIndex = Me.FrameSpriteCount() - Me.m_frameIndex
		Else
			Me.m_frameIndex = 0
		End If

		Me.firstTime = True
	End Sub


	Public Sub UpdateFrame(elapsed As Single, ByRef position As Position, ByRef spriteEffects__1 As SpriteEffects, ByRef playerState As PlayerState)
		'Resetting Name
		Dim TimePerFrame As Single = 0
		playerState.Value().Name = String.Empty
		'playerState.Value().Name = sequence.frames[frameIndex].name;
		'System.Console.WriteLine(playerState.Value().Name.ToUpper());


		TimePerFrame = frameRate + sequence.frames(m_frameIndex).delay
		'0.1
		'TimePerFrame = 0.9f + sequence.frames[frameIndex].delay; //0.1
		'TimePerFrame = 1.2f + sequence.frames[frameIndex].delay; //0.1
		TotalElapsed += elapsed

		If TotalElapsed > TimePerFrame Then
			'Play Sound
			sequence.frames(m_frameIndex).PlaySound()


			m_frameIndex = Math.Min(m_frameIndex + 1, Frames.Count - 1)
			TotalElapsed -= TimePerFrame

			'Taking name of the frame usefull for hit combat..
			playerState.Value().Name = sequence.frames(m_frameIndex).name

			If sequence.frames(m_frameIndex).type <> Enumeration.TypeFrame.SPRITE Then

				'COMMAND
				Dim aCommand As String() = sequence.frames(m_frameIndex).name.Split("|"C)
				Dim aParameter As String() = sequence.frames(m_frameIndex).parameter.Split("|"C)
				For x As Integer = 0 To aCommand.Length - 1
					If aCommand(x) = Enumeration.TypeCommand.ABOUTFACE.ToString() Then
						If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
							spriteEffects__1 = SpriteEffects.None
						Else
							spriteEffects__1 = SpriteEffects.FlipHorizontally
						End If
					ElseIf aCommand(x) = Enumeration.TypeCommand.GOTOFRAME.ToString() Then
						Dim par As String = aParameter(x)
						Dim result As Integer = sequence.frames.FindIndex(Function(f As Frame) f.name = par)
						m_frameIndex = result
					ElseIf aCommand(x) = Enumeration.TypeCommand.GOTOSEQUENCE.ToString() Then
						Dim par As String() = aParameter(x).Split(":"C)
						Dim result As Sequence = lsequence.Find(Function(s As Sequence) s.name = par(0))
						sequence = result
						m_frameIndex = 0

						If par.Length > 1 Then
							Dim v As New Vector2(Single.Parse(par(1).Split(","C)(0)), Single.Parse(par(1).Split(","C)(1)))
							playerState.Add(StatePlayerElement.Parse(par(0)), Enumeration.PriorityState.Normal, v)
						Else
							playerState.Add(StatePlayerElement.Parse(par(0)))
						End If
					ElseIf aCommand(x) = Enumeration.TypeCommand.IFGOTOSEQUENCE.ToString() Then
						Dim par As String = String.Empty
						If playerState.Value().IfTrue = True Then
							par = aParameter(0)
						Else
							par = aParameter(1)
						End If

						Dim result As Sequence = lsequence.Find(Function(s As Sequence) s.name = par)
						sequence = result
						m_frameIndex = 0
						playerState.Add(StatePlayerElement.Parse(par))
					ElseIf aCommand(x) = Enumeration.TypeCommand.DELETE.ToString() Then
						playerState.Add(Enumeration.State.delete, Enumeration.PriorityState.Force)

					End If
				Next
			End If

			Dim flip As Integer
			If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
				'& TotalElapsed > TimePerFrame) skip first frame =!==!=?!?bug
				flip = 1
			Else
				flip = -1
			End If





			position.Value = New Vector2(position.X + (sequence.frames(m_frameIndex).xOffSet * flip), position.Y + sequence.frames(m_frameIndex).yOffSet)
		ElseIf firstTime = True Then
			'Play Sound
			sequence.frames(m_frameIndex).PlaySound()

			Dim flip As Integer
			If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
				flip = 1
			Else
				flip = -1
			End If

			position.Value = New Vector2(position.X + (sequence.frames(m_frameIndex).xOffSet * flip), position.Y + sequence.frames(m_frameIndex).yOffSet)


			firstTime = False
		End If
	End Sub

	Public Sub UpdateFrameTile(elapsed As Single, ByRef position As Position, ByRef spriteEffects__1 As SpriteEffects, ByRef tileState As TileState)
		Dim TimePerFrame As Single = 0
		TimePerFrame = frameRate + sequence.frames(m_frameIndex).delay
		'0.1
		'TimePerFrame = 0.9f + sequence.frames[frameIndex].delay; //0.1
		TotalElapsed += elapsed

		If TotalElapsed > TimePerFrame Then
			'Play Sound
			sequence.frames(m_frameIndex).PlaySound()


			m_frameIndex = Math.Min(m_frameIndex + 1, Frames.Count - 1)
			TotalElapsed -= TimePerFrame




			If sequence.frames(m_frameIndex).type <> Enumeration.TypeFrame.SPRITE Then
				'COMMAND
				Dim aCommand As String() = sequence.frames(m_frameIndex).name.Split("|"C)
				Dim aParameter As String() = sequence.frames(m_frameIndex).parameter.Split("|"C)
				For x As Integer = 0 To aCommand.Length - 1
					If aCommand(x) = Enumeration.TypeCommand.ABOUTFACE.ToString() Then
						If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
							spriteEffects__1 = SpriteEffects.None
						Else
							spriteEffects__1 = SpriteEffects.FlipHorizontally
						End If
					ElseIf aCommand(x) = Enumeration.TypeCommand.GOTOFRAME.ToString() Then
						Dim par As String = aParameter(x)
						Dim result As Integer = sequence.frames.FindIndex(Function(f As Frame) f.name = par)
						m_frameIndex = result
					ElseIf aCommand(x) = Enumeration.TypeCommand.GOTOSEQUENCE.ToString() Then
						Dim par As String = aParameter(x)
						Dim result As Sequence = lsequence.Find(Function(s As Sequence) s.name = par)
						sequence = result
						m_frameIndex = 0
						tileState.Add(StateTileElement.Parse(par))
					ElseIf aCommand(x) = Enumeration.TypeCommand.IFGOTOSEQUENCE.ToString() Then
						Dim par As String = String.Empty
						If tileState.Value().IfTrue = True Then
							par = aParameter(0)
						Else
							par = aParameter(1)
						End If

						Dim result As Sequence = lsequence.Find(Function(s As Sequence) s.name = par)
						sequence = result
						m_frameIndex = 0
						tileState.Add(StateTileElement.Parse(par))

					End If
				Next
			End If

			Dim flip As Integer
			If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
				flip = 1
			Else
				flip = -1
			End If

			position.Value = New Vector2(position.X + (sequence.frames(m_frameIndex).xOffSet * flip), position.Y + sequence.frames(m_frameIndex).yOffSet)
		ElseIf firstTime = True Then
			Dim flip As Integer
			If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
				flip = 1
			Else
				flip = -1
			End If

			position.Value = New Vector2(position.X + (sequence.frames(m_frameIndex).xOffSet * flip), position.Y + sequence.frames(m_frameIndex).yOffSet)
			firstTime = False

			'Play Sound

			sequence.frames(m_frameIndex).PlaySound()
		End If

	End Sub


	Public Sub UpdateFrameItem(elapsed As Single, ByRef position As Position, ByRef spriteEffects__1 As SpriteEffects, ByRef itemState As TileState)
		Dim TimePerFrame As Single = 0
		TimePerFrame = frameRate + sequence.frames(m_frameIndex).delay
		'0.1
		'TimePerFrame = 0.9f + sequence.frames[frameIndex].delay; //0.1
		TotalElapsed += elapsed

		If TotalElapsed > TimePerFrame Then

			'Play Sound
			sequence.frames(m_frameIndex).PlaySound()


			m_frameIndex = Math.Min(m_frameIndex + 1, Frames.Count - 1)
			TotalElapsed -= TimePerFrame




			If sequence.frames(m_frameIndex).type <> Enumeration.TypeFrame.SPRITE Then
				'COMMAND
				Dim aCommand As String() = sequence.frames(m_frameIndex).name.Split("|"C)
				Dim aParameter As String() = sequence.frames(m_frameIndex).parameter.Split("|"C)
				For x As Integer = 0 To aCommand.Length - 1
					If aCommand(x) = Enumeration.TypeCommand.ABOUTFACE.ToString() Then
						If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
							spriteEffects__1 = SpriteEffects.None
						Else
							spriteEffects__1 = SpriteEffects.FlipHorizontally
						End If
					ElseIf aCommand(x) = Enumeration.TypeCommand.GOTOFRAME.ToString() Then
						Dim par As String = aParameter(x)
						Dim result As Integer = sequence.frames.FindIndex(Function(f As Frame) f.name = par)
						m_frameIndex = result
					ElseIf aCommand(x) = Enumeration.TypeCommand.GOTOSEQUENCE.ToString() Then
						Dim par As String = aParameter(x)
						Dim result As Sequence = lsequence.Find(Function(s As Sequence) s.name = par)
						sequence = result
						m_frameIndex = 0
						itemState.Add(StateTileElement.Parse(par))
					ElseIf aCommand(x) = Enumeration.TypeCommand.IFGOTOSEQUENCE.ToString() Then
						Dim par As String = String.Empty
						If itemState.Value().IfTrue = True Then
							par = aParameter(0)
						Else
							par = aParameter(1)
						End If

						Dim result As Sequence = lsequence.Find(Function(s As Sequence) s.name = par)
						sequence = result
						m_frameIndex = 0
						itemState.Add(StateTileElement.Parse(par))

					End If
				Next
			End If

			Dim flip As Integer
			If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
				flip = 1
			Else
				flip = -1
			End If

			position.Value = New Vector2(position.X + (sequence.frames(m_frameIndex).xOffSet * flip), position.Y + sequence.frames(m_frameIndex).yOffSet)
		ElseIf firstTime = True Then
			'Play Sound
			sequence.frames(m_frameIndex).PlaySound()


			Dim flip As Integer
			If spriteEffects__1 = SpriteEffects.FlipHorizontally Then
				flip = 1
			Else
				flip = -1
			End If

			position.Value = New Vector2(position.X + (sequence.frames(m_frameIndex).xOffSet * flip), position.Y + sequence.frames(m_frameIndex).yOffSet)
			firstTime = False
		End If

	End Sub



	Public Sub DrawTile(gameTime As GameTime, spriteBatch As SpriteBatch, position As Vector2, spriteEffects As SpriteEffects, depth As Single, texture As Texture2D)
		' Calculate the source rectangle of the current frame.
		Dim source As New Rectangle(0, 0, texture.Width, texture.Height)

		' Draw the current tile.
		spriteBatch.Draw(texture, position, source, Color.White, 0F, Vector2.Zero, _
			1F, spriteEffects, depth)
	End Sub


	Public Sub DrawTile(gameTime As GameTime, spriteBatch As SpriteBatch, position As Vector2, spriteEffects As SpriteEffects, depth As Single)
		' Calculate the source rectangle of the current frame.
		Dim source As New Rectangle(0, 0, sequence.frames(m_frameIndex).texture.Width, sequence.frames(m_frameIndex).texture.Height)

		' Draw the current tile.
		spriteBatch.Draw(sequence.frames(m_frameIndex).texture, position, source, Color.White, 0F, Vector2.Zero, _
			1F, spriteEffects, depth)
	End Sub


	Public Sub DrawTile(gameTime As GameTime, spriteBatch As SpriteBatch, position As Vector2, spriteEffects As SpriteEffects, depth As Single, rectangleMask As Rectangle, _
		texture As Texture2D)

		' Draw the current tile.
		spriteBatch.Draw(sequence.frames(m_frameIndex).texture, position, rectangleMask, Color.White, 0F, Vector2.Zero, _
			1F, spriteEffects, depth)

	End Sub

	Public Sub DrawTileMask(gameTime As GameTime, spriteBatch As SpriteBatch, position As Vector2, spriteEffects As SpriteEffects, depth As Single, rectangleMask As Rectangle)
		' Calculate the source rectangle of the current frame.
		'128-62
		'148-20
		'Rectangle source = new Rectangle(0, 128, 62, 20);
		'position.Y = position.Y + 128;
		'Rectangle source = new Rectangle(62, 20, sequence.frames[frameIndex].texture.Width, sequence.frames[frameIndex].texture.Height);

		' Draw the current tile.
		spriteBatch.Draw(sequence.frames(m_frameIndex).texture, position, rectangleMask, Color.White, 0F, Vector2.Zero, _
			1F, spriteEffects, depth)
	End Sub


	Public Sub DrawItem(gameTime As GameTime, spriteBatch As SpriteBatch, position As Vector2, spriteEffects As SpriteEffects, depth As Single)
		' Calculate the source rectangle of the current frame.
		Dim source As New Rectangle(0, 0, sequence.frames(m_frameIndex).texture.Width, sequence.frames(m_frameIndex).texture.Height)

		' Draw the current tile.
		spriteBatch.Draw(sequence.frames(m_frameIndex).texture, position, source, Color.White, 0F, Vector2.Zero, _
			1F, spriteEffects, depth)
	End Sub


	Public Sub DrawSprite(gameTime As GameTime, spriteBatch As SpriteBatch, position As Vector2, spriteEffects As SpriteEffects, depth As Single)

		' Calculate the source rectangle of the current frame.
		Dim source As New Rectangle(0, 0, sequence.frames(m_frameIndex).texture.Height, sequence.frames(m_frameIndex).texture.Height)

		position = New Vector2(position.X + Tile.PERSPECTIVE, position.Y - Tile.GROUND)
		'position = new Vector2(position.X + Tile.PERSPECTIVE, position.Y );

		'Maze.dEffect.Parameters["FromColor"].SetValue(Color.White);
		'Maze.dEffect.Parameters["ToColor"].SetValue(Color.Red);

		'spriteBatch.Begin(0, BlendState.Opaque, SamplerState.PointWrap, DepthStencilState.Default, RasterizerState.CullNone, Maze.dEffect);

		' Draw the current frame.
		spriteBatch.Draw(sequence.frames(m_frameIndex).texture, position, source, Color.White, 0F, Vector2.Zero, _
			1F, spriteEffects, depth)


	End Sub


	Public Sub DrawSprite(gameTime As GameTime, spriteBatch As SpriteBatch, position As Vector2, spriteEffects As SpriteEffects, depth As Single, texture As Texture2D)
		' Calculate the source rectangle of the current frame.
		Dim source As New Rectangle(0, 0, texture.Width, texture.Height)

		position = New Vector2(position.X + Tile.PERSPECTIVE + (texture.Width \ 2), position.Y - Tile.GROUND + (texture.Height \ 2))

		'Begin
		'spriteBatch.Begin();

		' Draw 
		spriteBatch.Draw(texture, position, source, Color.White, 0F, Vector2.Zero, _
			1F, spriteEffects, depth)

		'spriteBatch.End();

	End Sub


End Structure






